package com.market.common.util;

import com.fasterxml.uuid.EthernetAddress;
import com.fasterxml.uuid.UUIDTimer;
import com.fasterxml.uuid.ext.FileBasedTimestampSynchronizer;
import com.fasterxml.uuid.impl.TimeBasedGenerator;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public final class UUIDGenerator {

    public static final String LOCAL_MAC;
    private static final FileBasedTimestampSynchronizer SYNCHRONIZER;
    private static final EthernetAddress ADDRESS;

    public enum GenMethod {
        USE_NANO_TIME,
        USE_SET_SEED
    }

    static {
        LOCAL_MAC = getLocalMac();
        SYNCHRONIZER = getSynchronizer();
        ADDRESS = new EthernetAddress(LOCAL_MAC);
        try {
            UUIDTimer timer = new UUIDTimer(new Random(), SYNCHRONIZER);
            generator = new TimeBasedGenerator(ADDRESS, timer);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 1：nano
     * 2：UUID
     *
     * @param genMethod
     * @return
     */
    public static long generate(GenMethod genMethod) {
        if (LOCAL_MAC == null || SYNCHRONIZER == null) {
            return genernateByNanoTime();
        }

        switch (genMethod) {
            case USE_NANO_TIME:
                return genernateByNanoTime();
            case USE_SET_SEED:
                try {
                    return generateByUUID();
                } catch (IOException e) {
                    return genernateByNanoTime();
                }
            default:
                throw new IllegalArgumentException();
        }
    }

    /**
     * @Title: generateByUUID
     * @Description: 生成long型uuid
     * @return long
     * @throws IOException
     */
    private static long genernateByNanoTime() {
        return System.nanoTime();
    }

    private static long generateByUUID() throws IOException {
        TimeBasedGenerator generator = getGenerator();
        UUID uuid = generator.generate();
        return Math.abs(uuid.timestamp() * uuid.node());
    }

    private static volatile TimeBasedGenerator generator;
    private static final AtomicInteger count = new AtomicInteger(0);
    public static final int HIGH_WATHER_LEVEL = 1000;
    private static final ReentrantLock unfair = new ReentrantLock(false);

    private static TimeBasedGenerator getGenerator() throws IOException {
        int current = count.incrementAndGet();
        if (current > 1000) {
            boolean success = false;
            try {
                if (unfair.tryLock()) {
                    success = true;
                    UUIDTimer timer = new UUIDTimer(new Random(), SYNCHRONIZER);
                    generator = new TimeBasedGenerator(ADDRESS, timer);
                    count.set(0);
                    return generator;
                } else {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return getGenerator();
                }
            } finally {
                if (success) {
                    unfair.unlock();
                }
            }
        } else {
            return generator;
        }
    }

    private static FileBasedTimestampSynchronizer getSynchronizer() {
        try {
            FileBasedTimestampSynchronizer synchronizer = new FileBasedTimestampSynchronizer();
            return synchronizer;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getLocalMac() {
        byte[] mac = null;
        try {
            mac = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()).getHardwareAddress();
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
        StringBuffer sb = new StringBuffer("");
        for (int i = 0; i < mac.length; i++) {
            if (i != 0) {
                sb.append(":");
            }
            int temp = mac[i] & 0xff;
            String str = Integer.toHexString(temp);
            if (str.length() == 1) {
                sb.append("0" + str);
            } else {
                sb.append(str);
            }
        }
        return sb.toString();
    }
    
}
